/**
 * README
 * 
 * Modificacion de la biblioteca jWave disponible en http://code.google.com/p/jwave/
 * Convertidos los tipos de datos a float para agilizar el tratamiendo de grandes arrays de datos
 * como son los que se tratan en analisis genomicos.
 * Los autores originales de las funciones y del código de la biblioteca cuando fue seleccionada en
 * Abril de 2012 eran:
 *        Christian Scheiblich
 *        Thomas Leduc
 *        sashi
 *        Pol Kennel
 *        Thomas Haider
 *        
 * Por supuesto, todo el merito, honores y demas menciones sobre esta adaptacion les corresponden a ellos
 * y nada mas que a ellos.
 * 
 * 
 * This is a modified version of jWave, available in http://code.google.com/p/jwave/
 * It has been replace the double datatype with float for performance reasons in very big arrays used in
 * bioinformatics like genomic analysis.
 * Original authors of functions and the library when it has been selected in 2012's April were:
 *        Christian Scheiblich
 *        Thomas Leduc
 *        sashi
 *        Pol Kennel
 *        Thomas Haider
 *        
 * Of course, all the merits and mentions about this adaptation have corresponded to them.
 * 
 */

package ev.maths.handlers;

import ev.maths.handlers.wavelets.WaveletInterface;

/**
 * Clase abstracta base para el resto de wavelets
 * 
 * @date 01.08.2012
 * @author Enrique Vázquez de Luis
 */
public abstract class WaveletTransform extends BasicTransform {


  protected WaveletInterface _wavelet;

  protected int _iteration = -1;

  /**
   * Constructor sin parámetros
   *
   * @date 01.08.2012
   * @author Enrique Vázquez de Luis
   * @param wavelet La wavelet madre que usará
   */
  public WaveletTransform( ) {
  }
  
  /**
   * Constructor que recibe la wavelet madre
   *
   * @date 01.08.2012
   * @author Enrique Vázquez de Luis
   * @param wavelet
   */
  public WaveletTransform( WaveletInterface wavelet ) {
    _wavelet = wavelet;
  }

  /**
   * Constructor que recibe la wavelet madre y las iteraciones a realizar
   *
   * @date 01.08.2012
   * @author Enrique Vázquez de Luis
   * @param wavelet
   */
  public WaveletTransform( WaveletInterface wavelet, int iteration ) {
    _wavelet = wavelet;
    _iteration = iteration;
  }

  /**
   * Getter de la wavelet a utilizar 
   * 
   * @date 01.08.2012
   * @author Enrique Vázquez de Luis
   * @see math.transform.jwave.handlers.BasicTransform#forward(double[])
   */
  public WaveletInterface get_wavelet( ) {
    return _wavelet;
  }

  /**
   * Setter de la wavelet a utilizar
   * 
   * @date 01.08.2012
   * @author Enrique Vázquez de Luis
   * @param _wavelet Wavelet a utilizar
   * @see math.transform.jwave.handlers.BasicTransform#forward(double[])
   */
  public void set_wavelet( WaveletInterface _wavelet ) {
    this._wavelet = _wavelet;
  }

  /**
   * Getter del número de iteraciones
   * 
   * @date 01.08.2012
   * @author Enrique Vázquez de Luis
   * @see math.transform.jwave.handlers.BasicTransform#forward(double[])
   */
  public int get_iteration( ) {
    return _iteration;
  }

  /**
   * Setter del número de iteraciones
   * 
   * @date 01.08.2012
   * @author Enrique Vázquez de Luis
   * @param _iteration Numero de iteraciones
   * @see math.transform.jwave.handlers.BasicTransform#forward(double[])
   */
  public void set_iteration( int _iteration ) {
    this._iteration = _iteration;
  }

  /**
   * Realiza la transformada unidimensional de wavelet
   * 
   * @date 01.08.2012
   * @author Enrique Vázquez de Luis
   * @param arrTime coeficientes del dominio del tiempo
   * @see math.transform.jwave.handlers.BasicTransform#forward(double[])
   */
  @Override
  public float[ ] forward( float[ ] arrTime ) {
    if( _iteration == -1 )
      return forwardWavelet( arrTime );
    else
      return forwardWavelet( arrTime, _iteration );
  }

  /**
   * Realiza la transformada inversa de wavelet
   * 
   * @date 01.08.2012
   * @author Enrique Vázquez de Luis
   * @param arrHilb coeficientes del dominio de Hilbert
   * @see math.transform.jwave.handlers.BasicTransform#reverse(double[])
   */
  @Override
  public float[ ] reverse( float[ ] arrFreq ) {
    if( _iteration == -1 )
      return reverseWavelet( arrFreq );
    else
      return reverseWavelet( arrFreq, _iteration );
  }

  /**
   * Realiza la transformada bidimensional de wavelet
   * 
   * @date 01.08.2012
   * @author Enrique Vázquez de Luis
   * @param arrTime coeficientes del dominio del tiempo
   * @see math.transform.jwave.handlers.BasicTransform#reverse(double[])
   */
  public float[ ][ ] forward( float[ ][ ] matTime ) {
    if( _iteration == -1 )
      return super.forward( matTime );
    else
      return forwardWavelet( matTime, _iteration );
  }

  /**
   * Realiza la transformada bidimensional inversa de wavelet
   * 
   * @date 01.08.2012
   * @author Enrique Vázquez de Luis
   * @param arrHilb coeficientes del dominio de Hilbert
   * @see math.transform.jwave.handlers.BasicTransform#reverse(double[])
   */
  public float[ ][ ] reverse( float[ ][ ] matFreq ) {
    if( _iteration == -1 )
      return super.reverse( matFreq );
    else
      return reverseWavelet( matFreq, _iteration );
  }

  /**
   * Realiza la transformada tridimensional de wavelet
   * 
   * @date 01.08.2012
   * @author Enrique Vázquez de Luis
   * @param arrTime coeficientes del dominio del tiempo
   * @see math.transform.jwave.handlers.BasicTransform#reverse(double[])
   */
  public float[ ][ ][ ] forward( float[ ][ ][ ] spcTime ) {
    if( _iteration == -1 )
      return super.forward( spcTime );
    else
      return forwardWavelet( spcTime, _iteration );
  }

  /**
   * Realiza la transformada tridimensional inversa de wavelet
   * 
   * @date 01.08.2012
   * @author Enrique Vázquez de Luis
   * @param arrHilb coeficientes del dominio de Hilbert
   * @see math.transform.jwave.handlers.BasicTransform#reverse(double[])
   */
  public float[ ][ ][ ] reverse( float[ ][ ][ ] spcFreq ) {
    if( _iteration == -1 )
      return super.reverse( spcFreq );
    else
      return reverseWavelet( spcFreq, _iteration );
  }

  /**
   * Realiza la transformada de wavelet
   * 
   * @date 01.08.2012
   * @author Enrique Vázquez de Luis
   * @param arrTime coeficientes del dominio del tiempo
   * @see math.transform.jwave.handlers.BasicTransform#reverse(double[])
   */
  public abstract float[ ] forwardWavelet( float[ ] arrTime );

  /**
   * Realiza la transformada de wavelet hasta un nivel
   * 
   * @date 01.08.2012
   * @author Enrique Vázquez de Luis
   * @param arrTime coeficientes del dominio del tiempo
   * @param toLevel nivel hasta el que realizar la transformada
   * @see math.transform.jwave.handlers.BasicTransform#forward(double[], int)
   */
  public abstract float[ ] forwardWavelet( float[ ] arrTime, int toLevel );

  /**
   * Realiza la transformada inversa de wavelet
   * 
   * @date 01.08.2012
   * @author Enrique Vázquez de Luis
   * @param arrHilb coeficientes del dominio de Hilbert
   * @see math.transform.jwave.handlers.BasicTransform#reverse(double[])
   */
  public abstract float[ ] reverseWavelet( float[ ] arrTime );

  /**
   * Realiza la transformada inversa de wavelet desde un nivel determinado
   * 
   * @date 01.08.2012
   * @author Enrique Vázquez de Luis
   * @param arrHilb coeficientes de Hilbert
   * @param fromLevel iteraciones a realizar
   * @see math.transform.jwave.handlers.BasicTransform#reverse(double[], int)
   */
  public abstract float[ ] reverseWavelet( float[ ] arrFreq, int fromLevel );

  /**
   * Realiza la transformada bidimensional de wavelet hasta un límite
   * 
   * @date 01.08.2012
   * @author Enrique Vázquez de Luis
   * @param matTime coeficientes de tiempo
   * @param toLevel número de iteraciones
   * @see math.transform.jwave.handlers.DiscreteWaveletTransform#forward(double[][])
   */
  public float[ ][ ] forwardWavelet( float[ ][ ] matTime, int toLevel ) {
    int noOfRows = matTime.length;
    int noOfCols = matTime[ 0 ].length;
    float[ ][ ] matHilb = new float[ noOfRows ][ noOfCols ];

    for( int i = 0; i < noOfRows; i++ ) { // rows
      float[ ] arrTime = new float[ noOfCols ];
      for( int j = 0; j < noOfCols; j++ )
        arrTime[ j ] = matTime[ i ][ j ];
      float[ ] arrHilb = forwardWavelet( arrTime, toLevel );
      for( int j = 0; j < noOfCols; j++ )
        matHilb[ i ][ j ] = arrHilb[ j ];
    }

    for( int j = 0; j < noOfCols; j++ ) { // cols
      float[ ] arrTime = new float[ noOfRows ];
      for( int i = 0; i < noOfRows; i++ )
        arrTime[ i ] = matHilb[ i ][ j ];
      float[ ] arrHilb = forwardWavelet( arrTime, toLevel );
      for( int i = 0; i < noOfRows; i++ )
        matHilb[ i ][ j ] = arrHilb[ i ];
    }

    return matHilb;
  } // forward

  /**
   * Realiza la transformada inversa bidimensional de wavelet desde un nivel
   * 
   * @date 01.08.2012
   * @author Enrique Vázquez de Luis
   * @param matHilb coeficientes de Hilbert
   * @param fromLevel nivel de origen
   * @see math.transform.jwave.handlers.DiscreteWaveletTransform#reverse(double[][])
   */
  public float[ ][ ] reverseWavelet( float[ ][ ] matFreq, int fromLevel ) {
    int noOfRows = matFreq.length;
    int noOfCols = matFreq[ 0 ].length;
    float[ ][ ] matTime = new float[ noOfRows ][ noOfCols ];

    for( int j = 0; j < noOfCols; j++ ) { // cols
      float[ ] arrFreq = new float[ noOfRows ];
      for( int i = 0; i < noOfRows; i++ )
        arrFreq[ i ] = matFreq[ i ][ j ];
      float[ ] arrTime = reverseWavelet( arrFreq, fromLevel );
      for( int i = 0; i < noOfRows; i++ )
        matTime[ i ][ j ] = arrTime[ i ];
    }

    for( int i = 0; i < noOfRows; i++ ) { // rows
      float[ ] arrFreq = new float[ noOfCols ];
      for( int j = 0; j < noOfCols; j++ )
        arrFreq[ j ] = matTime[ i ][ j ];
      float[ ] arrTime = reverseWavelet( arrFreq, fromLevel );
      for( int j = 0; j < noOfCols; j++ )
        matTime[ i ][ j ] = arrTime[ j ];
    }

    return matTime;
  }

  /**
   * Realiza la transformada tridimensional de wavelet hasta un límite
   * 
   * @date 01.08.2012
   * @author Enrique Vázquez de Luis
   * @param matTime coeficientes de tiempo
   * @param toLevel número de iteraciones
   * @see math.transform.jwave.handlers.DiscreteWaveletTransform#forward(double[][])
   */
  public float[ ][ ][ ] forwardWavelet( float[ ][ ][ ] spcTime, int toLevel ) {

    int noOfRows = spcTime.length; // first dimension
    int noOfCols = spcTime[ 0 ].length; // second dimension
    int noOfHigh = spcTime[ 0 ][ 0 ].length; // third dimension

    float[ ][ ][ ] spcHilb = new float[ noOfRows ][ noOfCols ][ noOfHigh ];

    for( int i = 0; i < noOfRows; i++ ) {

      float[ ][ ] matTime = new float[ noOfCols ][ noOfHigh ];

      for( int j = 0; j < noOfCols; j++ ) {

        for( int k = 0; k < noOfHigh; k++ ) {

          matTime[ j ][ k ] = spcTime[ i ][ j ][ k ];

        } // high

      } // cols

      float[ ][ ] matHilb = forwardWavelet( matTime, toLevel ); // 2-D forward

      for( int j = 0; j < noOfCols; j++ ) {

        for( int k = 0; k < noOfHigh; k++ ) {

          spcHilb[ i ][ j ][ k ] = matHilb[ j ][ k ];

        } // high

      } // cols

    } // rows

    for( int j = 0; j < noOfCols; j++ ) {

      for( int k = 0; k < noOfHigh; k++ ) {

        float[ ] arrTime = new float[ noOfRows ];

        for( int i = 0; i < noOfRows; i++ )
          arrTime[ i ] = spcHilb[ i ][ j ][ k ];

        float[ ] arrHilb = forwardWavelet( arrTime, toLevel ); // 1-D forward

        for( int i = 0; i < noOfRows; i++ )
          spcHilb[ i ][ j ][ k ] = arrHilb[ i ];

      } // high

    } // cols

    return spcHilb;
  } // forward

  /**
   * Realiza la transformada inversa tridimensional de wavelet desde un nivel
   * 
   * @date 01.08.2012
   * @author Enrique Vázquez de Luis
   * @param matHilb coeficientes de Hilbert
   * @param fromLevel nivel de origen
   * @see math.transform.jwave.handlers.DiscreteWaveletTransform#reverse(double[][])
   */
  public float[ ][ ][ ] reverseWavelet( float[ ][ ][ ] spcHilb, int fromLevel ) {

    int noOfRows = spcHilb.length; // first dimension
    int noOfCols = spcHilb[ 0 ].length; // second dimension
    int noOfHigh = spcHilb[ 0 ][ 0 ].length; // third dimension

    float[ ][ ][ ] spcTime = new float[ noOfRows ][ noOfCols ][ noOfHigh ];

    for( int i = 0; i < noOfRows; i++ ) {

      float[ ][ ] matHilb = new float[ noOfCols ][ noOfHigh ];

      for( int j = 0; j < noOfCols; j++ ) {

        for( int k = 0; k < noOfHigh; k++ ) {

          matHilb[ j ][ k ] = spcHilb[ i ][ j ][ k ];

        } // high

      } // cols

      float[ ][ ] matTime = reverseWavelet( matHilb, fromLevel ); // 2-D reverse

      for( int j = 0; j < noOfCols; j++ ) {

        for( int k = 0; k < noOfHigh; k++ ) {

          spcTime[ i ][ j ][ k ] = matTime[ j ][ k ];

        } // high

      } // cols

    } // rows

    for( int j = 0; j < noOfCols; j++ ) {

      for( int k = 0; k < noOfHigh; k++ ) {

        float[ ] arrHilb = new float[ noOfRows ];

        for( int i = 0; i < noOfRows; i++ )
          arrHilb[ i ] = spcTime[ i ][ j ][ k ];

        float[ ] arrTime = reverseWavelet( arrHilb, fromLevel ); // 1-D reverse

        for( int i = 0; i < noOfRows; i++ )
          spcTime[ i ][ j ][ k ] = arrTime[ i ];

      } // high

    } // cols

    return spcTime;
  } // reverse

}
